/***************************************************************************
* Additional implementation of "BIKE: Bit Flipping Key Encapsulation". 
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Written by Nir Drucker and Shay Gueron
* AWS Cryptographic Algorithms Group
* (ndrucker@amazon.com, gueron@amazon.com)
*
* The license is detailed in the file LICENSE.txt, and applies to this file.
* ***************************************************************************/

#include "utilities.h"
#include "stdio.h"

#define BITS_IN_QW 64ULL
#define BITS_IN_BYTE 8ULL

// Print a new line only if we prints in qw blocks
_INLINE_ void print_newline(IN const uint64_t qw_pos) {
#ifndef NO_NEWLINE
	if ((qw_pos % 4) == 3) {
		printf("\n    ");
	}
#endif
}

// Prints a QW in LE/BE in win/linux format
_INLINE_ void print_uint64(IN const uint64_t val) {
// If printing in BE is requried swap the order of bytes
#ifdef PRINT_IN_BE
	uint64_t tmp = bswap_64(val);
#else
	uint64_t tmp = val;
#endif

#ifdef WIN32
	printf("%.16I64x", tmp);
#else
	printf("%.16lx", tmp);
#endif

#ifndef NO_SPACE
	printf(" ");
#endif
}

// Last block requires a special handling as we should
// zero mask all the bits above the desired number.
// endien - 0 - BE, 1 - LE
// Return 1 if last block was printed else 0
_INLINE_ uint8_t print_last_block(IN const uint8_t *last_bytes,
                                  IN const uint32_t bits_num,
                                  IN const uint32_t endien) {
	// Floor of bits/64 the reminder is in the next QW
	const uint32_t qw_num = bits_num / BITS_IN_QW;

	// How many bits to pad with zero
	const uint32_t rem_bits = bits_num - (BITS_IN_QW * qw_num);

	// We read byte byte and not the whole QW to avoid reading bad memory address
	const uint32_t bytes_num = ((rem_bits % 8) == 0) ? rem_bits / BITS_IN_BYTE : 1 + rem_bits / BITS_IN_BYTE;

	// Must be signed for the LE loop
	int i;

	if (0 == rem_bits) {
		return 0;
	}

	// Mask unneeded bits
	const uint8_t last_byte = ((rem_bits % 8) == 0) ? last_bytes[bytes_num - 1] : last_bytes[bytes_num - 1] & MASK(rem_bits % 8);
	// BE
	if (0 == endien) {
		for (i = 0; (uint32_t) i < (bytes_num - 1); i++) {
			printf("%.2x", last_bytes[i]);
		}

		printf("%.2x", last_byte);

		for (i++; (uint32_t) i < sizeof(uint64_t); i++) {
			printf("__");
		}
	} else {
		for (i = sizeof(uint64_t) - 1; (uint32_t) i >= bytes_num; i--) {
			printf("__");
		}

		printf("%.2x", last_byte);

		for (i--; i >= 0; i--) {
			printf("%.2x", last_bytes[i]);
		}
	}

#ifndef NO_SPACE
	printf(" ");
#endif

	return 1;
}

void print_LE(IN const uint64_t *in, IN const uint32_t bits_num) {
	const uint32_t qw_num = bits_num / BITS_IN_QW;

	// Print the MSB QW
	uint32_t qw_pos = print_last_block((uint8_t *) &in[qw_num], bits_num, 1);

	// Print each 8 bytes separated by space (if required)
	for (int i = ((int) qw_num) - 1; i >= 0; i--, qw_pos++) {
		print_uint64(in[i]);
		print_newline(qw_pos);
	}

	printf("\n");
}

void print_BE(IN const uint64_t *in, IN const uint32_t bits_num) {
	const uint32_t qw_num = bits_num / BITS_IN_QW;

	// Print each 16 numbers separatly
	for (uint32_t i = 0; i < qw_num; ++i) {
		print_uint64(in[i]);
		print_newline(i);
	}

	// Print the MSB QW
	print_last_block((uint8_t *) &in[qw_num], bits_num, 0);

	printf("\n");
}
